package com.lcf.study.leetcode;

/**
 * Created by 哈兹米 on 2022-10-31.
 * 481. 神奇字符串
 * <p>
 * 神奇字符串 s 仅由 '1' 和 '2' 组成，并需要遵守下面的规则：
 * 神奇字符串 s 的神奇之处在于，串联字符串中 '1' 和 '2' 的连续出现次数可以生成该字符串。
 * s 的前几个元素是 s = "1221121221221121122……" 。如果将 s 中连续的若干 1 和 2 进行分组，可以得到 "1 22 11 2 1 22 1 22 11 2 11 22 ......" 。
 * 每组中 1 或者 2 的出现次数分别是 "1 2 2 1 1 2 1 2 2 1 2 2 ......" 。上面的出现次数正是 s 自身。
 * <p>
 * 给你一个整数 n ，返回在神奇字符串 s 的前 n 个数字中 1 的数目。
 * <p>
 * 输入：n = 6
 * 输出：3
 * 解释：神奇字符串 s 的前 6 个元素是 “122112”，它包含三个 1，因此返回 3 。
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode.cn/problems/magical-string
 */
public class _481_MagicalString {

    public static void main(String[] args) {
        _481_MagicalString magicalString = new _481_MagicalString();
        magicalString.magicalString(6);
    }

    /**
     * 思路与算法
     * <p>
     * 首先题目给出神奇字符串 ss 的定义：
     * <p>
     * 仅由 11 和 22 组成。
     * 串联字符串中 1 和 2 的连续出现的次数可以生成该字符串。
     * 并给出了字符串 s 的前几个元素：1221121221221121122 …。现在要求求出 s 的前 n 个数字中 1 的数目，1≤n≤10*10*10*10*10，
     * 那么我们可以按照定义来构造长度为 n 的字符串 s，然后统计 s 中 1 的个数即可。那么如何通过现有的开头字符串来构造剩下的字符串呢——我们可以初始化字符串 s = 122，
     * 用指针 i 来指向现在需要构造的对应的组的大小，用指针 j 来指向现在需要构造的对应组的位置，此时 i = 2，j = 3。
     * 因为相邻组中的数字一定不会相同，所以我们可以通过 j 的前一个位置的数来判断当前需要填入的组中的数字。又因为每组的大小只为 1 或者 2，这保证了 j > i 在构造的过程中一定成立，
     * 即在指针 j 处填入组时一定能确定此时需要填入的组的大小。这样我们就可以不断往下进行构造直到字符串长度到达 n。
     * <p>
     * 上述的过程中我们初始化字符串 s = 122，所以当 n < 4 时我们无需再往下构造，此时直接返回 1 即可。
     */
    public int magicalString(int n) {
        if (n < 4) {
            return 1;
        }
        char[] s = new char[n];
        s[0] = '1';
        s[1] = '2';
        s[2] = '2';
        int res = 1;
        int i = 2;
        int j = 3;
        while (j < n) {
            int size = s[i] - '0';
            int num = 3 - (s[j - 1] - '0');
            while (size > 0 && j < n) {
                s[j] = (char) ('0' + num);
                if (num == 1) {
                    res++;
                }
                j++;
                size--;
            }
            i++;
        }
        return res;
    }
}
